require "./simple_input"
require "./lib/graphics/sprite"
require "./lib/geometry/rectangle"

include Geometry
module DGame
  class Player
    ANIME_TIME = 3
    A_MIN = 0.1
    A = 0.5
    DASH_VMAX = 8.0
    DASH_MUL = 2.0
    JUMP_SPEED = 6.0
    KICK_SPEEDX = 6.0
    KICK_SPEEDY = 6.0
    LADDER_SPEED = 4.0
    VX_MAX = 4.0
    VY_MAX = 8.0
    SMOKE_TIME = 4
    SECOND_JUMP_TIME = 3
    JUMP_DURATION = 6
    DEAD_TIME = 40
    AIR_RESISTANCE = 0.25

    attr_reader :sprite
    def initialize(x, y)
      @width = 16
      @height = 16
      @half_width = @width / 2
      @half_height = @height / 2
      @sprite = Graphics::Sprite.new($res.get_texture("game/player"), x, y, :src_width => 16, :src_height => 16, :center_x => 8, :center_y => 8)
      
      @dx = 0
      @dy = 0
      @ax = 0
      @ay = 0
      @vx = 0
      @vy = 0
      
      @landed = false
      @kicking = false
      @dir_x = 1
      @dir_y = 1
      @anime_timer = 0
      @anime = 0
      @smoke_timer = 0
      @climbing = false
      @jumping = false
      @slipping = false
      @landed = false
      
		  @vx_max = 4.0
      @vy_max = 4.0

      @above_touching = false
      @below_touching = false
      @left_touching = false
      @right_touching = false
      
      @hit_x = false
      @hit_rect = Rectangle.new(4, 2, 8, 14)
      
      @gravity = 0.5

    end

    def climbing?
      return @climbing
    end
    
    def dashing?
      return @dashing
    end

    def kicking?
      return @kicking
    end

    def landed?
			return @landed
    end
    
    def jumping?
      return @jumping
    end
    
    def slipping?
      return @slipping
    end
    
		#煙を出すかどうか
		def smoking?
      return @smoke_timer == SMOKE_TIME
    end
    
		def set_smoke
      @smoke_timer = SMOKE_TIME;
    end

    def set_pose(pose)
      tx = 16
      ty = 16
      case pose
        when :walk_left1
          @sprite.src_x = 2 * tx
          @sprite.src_y = 0
        when :walk_left2
          @sprite.src_x = 3 * tx
          @sprite.src_y = 0
        when :walk_left3
          @sprite.src_x = 4 * tx
          @sprite.src_y = 0
        when :dash_left1
          @sprite.src_x = 5 * tx
          @sprite.src_y = 0
        when :dash_left2
          @sprite.src_x = 6 * tx
          @sprite.src_y = 0
        when :dash_left3
          @sprite.src_x = 7 * tx
          @sprite.src_y = 0
        when :jump_left
          @sprite.src_x = 8 * tx
          @sprite.src_y = 0
        when :climb1
          @sprite.src_x = 9 * tx
          @sprite.src_y = 0
        when :climb2
          @sprite.src_x = 10 * tx
          @sprite.src_y = 0
        when :climb3
          @sprite.src_x = 11 * tx
          @sprite.src_y = 0
        when :kick
          @sprite.src_x = 0
          @sprite.src_y = ty
      end
    end

    #プレイヤーのグラフィックをセット
    def set_graphic
      #横方向の拡大率（向き）
      @sprite.scale_x = -@dir_x

      #縦方向の拡大率（向き）
      @sprite.scale_y = @dir_y

      #地面についている場合
      if self.landed?
        if self.dashing?
          #ダッシュ
          self.set_pose(:dash_left2) if @anime == 0
          self.set_pose(:dash_left3) if @anime == 1
          self.set_pose(:dash_left2) if @anime == 2
          self.set_pose(:dash_left1) if @anime == 3
        else
          #歩行
          self.set_pose(:walk_left2) if @anime == 0
          self.set_pose(:walk_left3) if @anime == 1
          self.set_pose(:walk_left2) if @anime == 2
          self.set_pose(:walk_left1) if @anime == 3
        end
        
        #キャラクターが強制移動されている時
        if(self.slipping? || @rvx == 0)
          unless SimpleInput.pressed?(:right) || SimpleInput.pressed?(:left) || self.dashing?
            self.set_pose(:walk_left2)
          end
        end
      else
        #空中にいる場合
        if @vy < 0
          self.set_pose(:jump_left)
        end
        if @vy >= 0
          self.set_pose(:walk_left2)
        end
      end
    end

    def update(map_texture)
      self.move_init
      self.input
      self.pre_move
      self.collide(map_texture)
      self.move
      
			#一定間隔でアニメ状態をひとつ進める
      if @vx == 0
        @anime_timer = 0
        @anime = 0
      else
        @anime_timer += 1
  			if @anime_timer > ANIME_TIME
  				@anime += 1
  				@anime_timer = 0
  			end
  			@anime = 0 if @anime > 3
      end
      
      self.set_graphic
    end

    def move_init
			#重力によってプレイヤーの上下の向きを決定
			if(@gravity >= 0)
				@dir_y = 1
			else 
				@dir_y = -1
      end
			
			#X,Y方向の速度の最大値を決定
			@vx_max = VX_MAX
			@vy_max = VY_MAX

			#変移・加速度の初期化
			@ax = 0
			@ay = @gravity
    end
    
    def input
      if SimpleInput.pressed?(:left)
        @ax -= 1
        @dir_x = -1
      end
      
      if SimpleInput.pressed?(:right)
        @ax += 1
        @dir_x = 1
      end
      
      if self.landed?
        @ay -= @dir_y * 8 if SimpleInput.pressed_newly?(:space)
      end
    end

    # divide x, y movement into unit.
    def move_unit(dx, dy)
      
      return [0, 0, 0] if dx.round == 0 && dy.round == 0
      return [0, dy / dy.round.abs.to_f, dy.round.abs] if dx.round == 0
      return [dx / dx.round.abs.to_f, 0, dx.round.abs] if dy.round == 0

      if dx > dy
        ttx = dx / dy.round.abs.to_f
        tty = dy / dy.round.abs.to_f
        count = dy.round.abs
      else
        ttx = dx / dx.round.abs.to_f
        tty = dy / dx.round.abs.to_f
        count = dx.round.abs
      end
      
      return ttx, tty, count
    end

    def hit_top
      return @sprite.y.round + @hit_rect.top
    end
    
    def hit_right
      return @sprite.x.round + @hit_rect.right
    end
    
    def hit_bottom
      return @sprite.y.round + @hit_rect.bottom
    end
    
    def hit_left
      return @sprite.x.round + @hit_rect.left
    end
    
    def collide_horizontal(map_texture)
    
    end
    
    def collide(map_texture)
      #p "------------------"
      ux, uy, count = self.move_unit(@vx, @vy)
      #p "vx #{@vx} vy#{@vy} ux:#{ux} uy:#{uy} count:#{count}"
      if ux < 0
        ix = self.hit_left
      else
        ix = self.hit_right
      end

      if uy < 0
        iy = self.hit_top - 1
      else
        iy = self.hit_bottom
      end
      @landed = false
      dx = 0
      dy = 0

      gravity = 0.5
      gravity = -0.5 if map_texture[@sprite.x + @half_width, @sprite.y + @half_height] == YELLOW
      @gravity = gravity

      count.times do
        dx += ux
        dy += uy

        ht = self.hit_top + dy.round
        hb = self.hit_bottom + dy.round
      
        hr = self.hit_right + dx.round - 2
        hl = self.hit_left + dx.round + 2

        #vertical collision
        if uy != 0
          b = false
          (hl..hr).each do |v|
            c = map_texture[v, iy + dy.round]

            if c == GRAY
              dx -= ux
              dy -= uy
              uy = 0
              @landed = true
              b = true
              break
            end
          end
          redo if b
        end

        #horizontal collision
        if ux != 0
          b = false
          arr = @dir_y > 0 ? (ht..hb).to_a : (ht..hb).to_a.reverse
          arr.each do |v|
            c = map_texture[ix + dx.round, v]
  
            if c == GRAY

              # go up stairs
              if @dir_y > 0 && v >= hb - 5
                @landed = true
                @sprite.y += v - hb
                break
              end
              
              # go up stairs(reverse)
              if @dir_y < 0 && v <= ht + 5
                @landed = true
                @sprite.y += v - ht
                break
              end
              
              dx -= ux
              dy -= uy
              ux = 0
              b = true
              break
            end
          end
          redo if b
        end
      end
      
      @vx = dx
      @vy = dy
      @vx *= 0.5 if self.landed? && @ax == 0
    end
    
    def pre_move
			#仮移動における変移を決定
      @vx += @ax
      @vy += @ay

      if SimpleInput.pressed?(:z)
        @vx = [[@vx, 6].min, -6].max
      else
        @vx = [[@vx, 3].min, -3].max
      end
      
      if @gravity > 0
        @vy = [[@vy, 6].min, -10].max
      else
        @vy = [[@vy, 10].min, -6].max
      end
      
      @vx = 0 if @vx.abs < 0.1
      @vy = 0 if @vy.abs < 0.1

    end

    def move
			#最終的な位置を決定する
			@sprite.x += @vx
			@sprite.y += @vy
    end

    def render(s)
      @sprite.render(s)
    end
  end
end